package org.glossy.scaffolding
/**
 * User: Christian Köberle
 * Date: Nov 4, 2010
 * Time: 9:55:03 AM
 */

import org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass
import org.codehaus.groovy.grails.commons.GrailsClassUtils
import org.codehaus.groovy.grails.plugins.PluginManagerHolder

/**The SecureRenderer will generate code for the GSP-Templates and Controllers.
 * The SecureRenderer will generate special secure code if the project used the spring-securety-core or acegi plugin.
 *
 */
class SecureRenderer {

  static final def CLASSES = [update:'save']
  static final def MESSAGE_DEFAULT = [update:'Update']
  static final int TAB = 4
  DefaultGrailsDomainClass domainClass
  String className
  def secureRoles
  def out

  /**
   * Constuctor of the Class SecureRenderer
   * @param domainClass A DefaultGrailsDomainClass-Object to render for it
   * @param out The PrintWriter that writes the GSP-Templates.
   */
  public SecureRenderer(domainClass, out) {
    this.domainClass = domainClass
    className = domainClass.name
    secureRoles = GrailsClassUtils.getStaticPropertyValue(domainClass.getClazz(), 'secure')
    this.out = out
  }

  /**
   * Generate the navigation code for GSPs.
   * @param view The name of the view for rendering.
   */
  def createNavigation(view) {
    def spaces = 2*TAB
    out << '<div class="nav">\n'
    if(view != 'list')
      createSecuredLink('list', spaces)
    if (view != 'create'){}
       createSecuredLink('create', spaces)

    out << (' '*spaces) << '</div>'
  }

  /**
   * Generate the buttoms code for GSPs.
   * @param view The name of the view for rendering.
   */
  def createButtons(view) {
    def spaces = 5*TAB
    out << '<div class="buttons">\n'
    createSecuredButton(view, spaces)
    out << (' '*spaces) << '</div>'
  }

  /**
   * Create annotation @Secured for Controller class (use sector='all') or methods
   * in case of a sector ['all', 'show', 'list', 'create', 'edit', 'delete']
   * @param sector A String, one of  ['all', 'show', 'list', 'create', 'edit', 'delete']
   */
  def createSecureAnnotation(sector) {
    if (!useSecure(domainClass) || !(secureRoles[sector])) return
    out << "@Secured(['"
    out << secureRoles[sector].join("','")
    out << "'])"
  }

  /**
   * Create the import line for annotation @Secured for the Controller
   */
  def importSecuredAnnotation() {
    if (!useSecure(domainClass) || !(secureRoles?.any{!it.value.isEmpty()})) return
    if(isSpringSecure())
      out << "import grails.plugins.springsecurity.Secured"
    else if(acgi)
      out << "import org.codehaus.groovy.grails.plugins.springsecurity.Secured"
  }

  private void createSecuredLink(view, int spaces) {
    if (!useSecure(domainClass) || !(secureRoles.all || secureRoles[view]))
      createLink(view, spaces+4)
    else {
      openIfAnyGrantedTag(view, spaces+TAB)
      createLink(view, spaces+2*TAB)
      closeIfAnyGrantedTag(spaces+TAB)
    }
  }

  private void createSecuredButton(view, int spaces) {
    if (!useSecure(domainClass) || !(secureRoles.all || secureRoles[view]))
      createButton(view, spaces+TAB)
    else {
      openIfAnyGrantedTag(view, spaces+TAB)
      createButton(view, spaces+2*TAB)
      closeIfAnyGrantedTag(spaces+TAB)
    }
  }

  private void openIfAnyGrantedTag(sector, int spaces) {
    out << (' '*spaces)
    out << (isSpringSecure() ? '<sec' : '<g')
    out << ":ifAnyGranted roles=\"\${${getRoleItemsString(sector)}}\">\n"
  }

  private String closeIfAnyGrantedTag(int spaces) {
    out << (' '*spaces)
    out << (isSpringSecure() ? '</sec:ifAnyGranted>\n' : '</g:ifAnyGranted>\n')
  }

  private String getRoleItemsString(sector) {
    if (!secureRoles[sector] && !secureRoles.all) return ''
    if (sector == 'all' || (secureRoles.all && !secureRoles[sector]))
      "${className}.secure.all.join(',')"
    else if (secureRoles.all )
      "[${className}.secure.all,${className}.secure.${sector}].flatten().join()"
    else
      "${className}.secure.${sector}.join(',')"
  }

  private void createLink(view, int spaces) {
    out << (' '*spaces)
    out << '<span class="menuButton">'
    out << "<g:link class=\"${view}\" action=\"${view}\">"
    out << "<g:message code=\"default.${view == 'create' ? 'new' : view}.label\" args=\"[entityName]\" encodeAs=\"HTML\" />"
    out << '</g:link>'
    out << '</span>\n'
  }

  private void createButton(action, int spaces){
    out << (' '*spaces)
    out <<  '<span class="button">'
    out <<    "<g:actionSubmit class=\"${CLASSES[action]}\" action=\"${action}\" "
    out <<    "value=\"\${message(code: 'default.button.${action}.label', default: '${MESSAGE_DEFAULT[action]}', encodeAs:'HTML')}\" "
    if(action == 'delete')
      out <<  "onclick=\"return confirm('\${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?', encodeAs:'HTML')}');\""
    out <<    "/>"
    out <<  '</span>\n'
  }

  static def getSecureRoles(domainClass) {
    GrailsClassUtils.getStaticPropertyValue(domainClass.getClazz(), 'secure')
  }

  static def useSecure(domainClass) {
    getSecureRoles(domainClass) && (isSpringSecure() || isAcgi())
  }

  static boolean isSpringSecure() {
    PluginManagerHolder.getPluginManager().hasGrailsPlugin("spring-security-core")
  }

  static boolean isAcgi() {
    PluginManagerHolder.getPluginManager().hasGrailsPlugin("acegi")
  }
}
